package models.chat.engines;

import controllers.chat.ChatWS;
import controllers.chat.IdPair;
import models.ApplicationUser;
import models.chat.Channel;
import models.chat.messages.ChannelMessage;
import models.chat.messages.system.MemberChangedStatusMessage;
import models.chat.messages.system.MemberJoinedChannelMessage;
import models.chat.messages.system.MemberLeftChannelMessage;
import play.Logger;
import play.jobs.Job;
import play.libs.F;

import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;


/**
 * @author Konovalov_Nik
 */
@SuppressWarnings("SuspiciousMethodCalls")
public class MessageProcessor extends Job {
    private QueueMessage message;

    public MessageProcessor(QueueMessage message) {
        this.message = message;
    }

    @Override
    public void doJob() throws Exception {
        if (message instanceof ChannelMessage) {
            processChannelMessage((ChannelMessage) message);
        }
        if (message instanceof MemberChangedStatusMessage) {
            processUserChangedStatusMessage((MemberChangedStatusMessage) message);
        }
        if (message instanceof MemberJoinedChannelMessage) {
            processMemberJoinedChannelMessage((MemberJoinedChannelMessage) message);
        }
        if (message instanceof MemberLeftChannelMessage) {
            processMemberLeftChannelMessage((MemberLeftChannelMessage) message);
        }
    }

    private void processMemberLeftChannelMessage(MemberLeftChannelMessage message) {

        ApplicationUser current = ApplicationUser.findById(message.member.id);
        Channel channel = Channel.findById(message.channel.id);

        ChatWS.lock.readLock().lock();
        try {
            for (Map.Entry<IdPair, F.EventStream<QueueMessage>> streamEntry : ChatWS.toClientMessages.entrySet()) {
                if (streamEntry.getKey().chatId == channel.id && streamEntry.getKey().uid != current.id) {
                    streamEntry.getValue().publish(message);
                }
            }
        } finally {
            ChatWS.lock.readLock().unlock();
        }


    }

    private void processMemberJoinedChannelMessage(MemberJoinedChannelMessage message) {
        ApplicationUser current = ApplicationUser.findById(message.member.id);
        Channel channel = Channel.findById(message.channel.id);
        ChatWS.lock.readLock().lock();
        try {
            for (Map.Entry<IdPair, F.EventStream<QueueMessage>> streamEntry : ChatWS.toClientMessages.entrySet()) {
                if (streamEntry.getKey().chatId == channel.id && streamEntry.getKey().uid != current.id) {
                    streamEntry.getValue().publish(message);
                }
            }
        } finally {
            ChatWS.lock.readLock().unlock();
        }
    }

    private void processUserChangedStatusMessage(MemberChangedStatusMessage message) {
        ApplicationUser current = ApplicationUser.findById(message.member.id);
        current.status = message.newStatus;
        current.save();
        List<Channel> channels = current.getChannels();
        ChatWS.lock.readLock().lock();
        try {
            for (Map.Entry<IdPair, F.EventStream<QueueMessage>> streamEntry : ChatWS.toClientMessages.entrySet()) {
                if (channels.contains(streamEntry.getKey().chatId)) {
                    streamEntry.getValue().publish(message);
                }
            }
        } finally {
            ChatWS.lock.readLock().unlock();
        }
    }

    private void processChannelMessage(ChannelMessage message) {
        message.save();
        Channel channel = Channel.findById(message.to.id);
        channel.messageIdList.add(message.id);
        for (ApplicationUser to : channel.getMembers()) {
            publishChatMessageEvent(to.id, channel.id, message);
        }
        channel.save();
    }

    private void publishChatMessageEvent(Long memberId, Long channelId, QueueMessage message) {
        IdPair pair = new IdPair(memberId, channelId);
        ChatWS.lock.readLock().lock();
        try {
            if (!ChatWS.toClientMessages.containsKey(pair)) {
                return;
            }
            ChatWS.toClientMessages.get(pair).publish(message);
        } finally {
            ChatWS.lock.readLock().unlock();
        }
    }
}
